/*
 *    Copyright © OpenAtom Foundation.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.inspur.edp.cef.spi.jsonser.abstractcefchange;


import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.inspur.edp.cef.api.manager.serialize.CefSerializeContext;
import com.inspur.edp.cef.entity.changeset.AbstractModifyChangeDetail;
import com.inspur.edp.cef.entity.changeset.AddChangeDetail;
import com.inspur.edp.cef.entity.changeset.AddOrModifyChangeDetail;
import com.inspur.edp.cef.entity.changeset.DeleteChangeDetail;
import com.inspur.edp.cef.entity.changeset.IChangeDetail;
import com.inspur.edp.cef.entity.entity.IEntityData;
import com.inspur.edp.cef.entity.entity.IMultiLanguageData;
import com.inspur.edp.cef.spi.extend.IDataExtendSerializerItem;
import com.inspur.edp.cef.spi.extend.entity.ICefEntityExtend;
import com.inspur.edp.cef.spi.jsonser.base.StringUtils;
import com.inspur.edp.cef.spi.jsonser.valueobj.CommonValueObjChangeSerializer;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;

public abstract class AbstractCefChangeSerializer<T extends AbstractCefDataSerItem> extends
    JsonSerializer<IChangeDetail> {

   protected static final String DetailChangeType = "ChangeType", ChangeInfo = "ChangeInfo", DataId = "DataId", ChangeDetail = "ChangeDetail", PropertyChanges = "PropertyChanges", ChildChanges = "ChildChanges", NodeCode = "NodeCode", NodeChanges = "NodeChanges";

   protected java.util.List<T> serializers;

   public AbstractCefChangeSerializer(java.util.List<T> serializers) {
      this.serializers = serializers;
   }

   protected java.util.List<T> getSerializerItems() {
      return serializers;
   }

   @Override
   public void serialize(IChangeDetail changeDetail, JsonGenerator jsonGenerator,
       SerializerProvider serializerProvider) throws IOException {
      writeJson(changeDetail, jsonGenerator, serializerProvider);

   }

   public void writeJson(IChangeDetail changeDetail, JsonGenerator jsonGenerator,
       SerializerProvider serializerProvider) throws IOException {
      if (changeDetail == null) {
         return;
      }
      jsonGenerator.writeStartObject();
      writeChangeType(jsonGenerator, changeDetail, serializerProvider);
      jsonGenerator.writeFieldName(ChangeInfo);
      writeChangeDetailJson(jsonGenerator, changeDetail, serializerProvider);
      jsonGenerator.writeEndObject();
   }

   private void writeChangeType(JsonGenerator jsonGenerator, IChangeDetail changeDetail,
       SerializerProvider serializerProvider) throws IOException {
      jsonGenerator.writeFieldName(DetailChangeType);
      jsonGenerator.writeString(String.valueOf(changeDetail.getChangeType()));
   }

   private void writeChangeDetailJson(JsonGenerator jsonGenerator, IChangeDetail value,
       SerializerProvider serializerProvider) throws IOException {
      if (value instanceof AddChangeDetail) {
         innerWriteAddJson(jsonGenerator,
             (AddChangeDetail) ((value instanceof AddChangeDetail) ? value : null),
             serializerProvider);
      } else if (value instanceof DeleteChangeDetail) {
         innerWriteDeleteJson(jsonGenerator,
             (DeleteChangeDetail) ((value instanceof DeleteChangeDetail) ? value : null),
             serializerProvider);
      } else if(value instanceof AddOrModifyChangeDetail) {
         innerWriteAddOrModifyJson(jsonGenerator, (AddOrModifyChangeDetail)value, serializerProvider);
      } else {
         innerWriteModifyJson(jsonGenerator,
             (AbstractModifyChangeDetail) ((value instanceof AbstractModifyChangeDetail) ? value
                 : null), serializerProvider);
      }
   }

   private void innerWriteAddJson(JsonGenerator jsonGenerator, AddChangeDetail detail,
       SerializerProvider serializerProvider) throws IOException {
      IEntityData data = detail.getEntityData();
      if (getCefDataConvertor() != null) {
         getCefDataConvertor().writeJson(jsonGenerator, data, serializerProvider);
      } else {
         jsonGenerator.writeObject(data);
      }
      //writer.WriteEndObject();
   }

   private void innerWriteAddOrModifyJson(JsonGenerator jsonGenerator, AddOrModifyChangeDetail detail,
       SerializerProvider serializerProvider) throws IOException {
      innerWriteModifyJson(jsonGenerator, detail.getModifyChange(), serializerProvider);
   }

   private void innerWriteDeleteJson(JsonGenerator jsonGenerator, DeleteChangeDetail detail,
       SerializerProvider serializerProvider) throws IOException {
      jsonGenerator.writeStartObject();
      jsonGenerator.writeFieldName(DataId);
      jsonGenerator.writeString(detail.getID());
      jsonGenerator.writeEndObject();
   }

   private void innerWriteModifyJson(JsonGenerator jsonGenerator, AbstractModifyChangeDetail detail,
       SerializerProvider serializerProvider) throws IOException {
      jsonGenerator.writeStartObject();
      //writer.WritePropertyName(PropertyChanges);
      writeModifyJson(jsonGenerator, detail, serializerProvider);
      jsonGenerator.writeEndObject();
   }

   private void writeMultiLang(JsonGenerator jsonGenerator, AbstractModifyChangeDetail detail, SerializerProvider serializerProvider)
       throws IOException {
      MultiLangSerializationUtil.serialize(jsonGenerator, detail);
   }

   protected void writeModifyJson(JsonGenerator jsonGenerator, AbstractModifyChangeDetail detail,
       SerializerProvider serializerProvider) throws IOException {
      for (Map.Entry<String, Object> propertyChange : detail.getPropertyChanges().entrySet()) {
         writeModifyPropertyJson(jsonGenerator, propertyChange.getKey(), propertyChange.getValue(),
             serializerProvider);
      }
      writeMultiLang(jsonGenerator, detail, serializerProvider);
   }

   //	/**
//	 将Modify数据写入到Json中
//
//	 @param writer Json写入器
//	 @param propertyName 属性名字
//	 @param value 属性值
//	 @param serializer Json序列化器
//	*/
   private void writeModifyPropertyJson(JsonGenerator jsonGenerator, String propertyName,
       Object value, SerializerProvider serializerProvider) throws IOException {
      boolean hasWriten = false;
      for (AbstractCefDataSerItem item : getSerializerItems()) {
         if (item instanceof IDataExtendSerializerItem
             && !org.springframework.util.StringUtils.isEmpty(configId)) {
            ((IDataExtendSerializerItem) item).setConfigId(configId);
         }
         hasWriten = item
             .writeModifyPropertyJson(jsonGenerator, StringUtils.toCamelCase(propertyName), value,
                 serializerProvider);
         if (hasWriten) {
            break;
         }
      }
      if (hasWriten == false) {
         writeUnkownChange(jsonGenerator, propertyName, value, serializerProvider);
         //throw new RuntimeException(String.format("未识别的变更名称：%1$s。", propertyName));
      }
   }

   protected AbstractCefDataSerializer getCefDataConvertor() {
      return null;
   }

   protected void writeUnkownChange(JsonGenerator jsonGenerator, String propertyName,
       Object value, SerializerProvider serializerProvider) {
      try {
         jsonGenerator.writeFieldName(StringUtils.toCamelCase(propertyName));
         if (value instanceof Date) {
            BefDateSerUtil.getInstance().writeDateTime(jsonGenerator, (Date) value);
         } else if (value instanceof AbstractModifyChangeDetail) {
            getDefaultNestedChangeSerializer()
                .serialize((IChangeDetail) value, jsonGenerator, serializerProvider);
         } else {
            serializerProvider.defaultSerializeValue(value, jsonGenerator);
         }
      } catch (IOException e) {
         throw new RuntimeException(e);
      }
   }

   protected JsonSerializer<IChangeDetail> getDefaultNestedChangeSerializer() {
      return new CommonValueObjChangeSerializer();
   }

   private String configId;

   @Deprecated
   public void setConfigId(String value) {
      configId = value;
   }

   private List<ICefEntityExtend> extendList;

   public void addExtend(ICefEntityExtend extList) {
      Objects.requireNonNull(extList);

      if (extendList == null) {
         extendList = new ArrayList<>();
      }
      extendList.add(extList);
   }

   protected List<ICefEntityExtend> getExtendList() {
      return extendList == null ? Collections.emptyList() : extendList;
   }

   public void setCefSerializeContext(CefSerializeContext context) {
    if (this.serializers != null && this.serializers.size() > 0) {
      for (T item : serializers
      ) {
        item.setCefSerializeContext(context);
      }
    }
  }
}
